package com.gecba.system.service;

import com.gecba.modular.jpa.utils.StringUtils;
import org.apache.commons.beanutils.PropertyUtils;
import org.springframework.data.jpa.domain.Specification;

import javax.persistence.criteria.Predicate;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.math.BigDecimal;
import java.util.*;
import java.util.stream.Collectors;

/**
 *
 * @autor chenBo
 * @className SpecificationImpl
 * @date 2021/12/17
 */
public class SpecificationImpl {

    private static final String START = "_start";
    private static final String END = "_end";
    private static final String AFTER = "_after";
    private static final String BEFORE = "_before";
    private static final String INCLUDE = "_include";

    public static <T> Specification<T> specification(T entity) {
        return (root, query, criteriaBuilder) -> {

            List<PropertyDescriptor> propertyDescriptors = Arrays.stream(PropertyUtils.getPropertyDescriptors(entity))
                    .filter(Objects::nonNull).collect(Collectors.toList());

            List<Predicate> list = new ArrayList<>();

            propertyDescriptors.forEach(propertyDescriptor -> {
                try {
                    String name = propertyDescriptor.getName();

                    if (propertyDescriptor.getPropertyType().equals(String.class)) {
                        if (PropertyUtils.isReadable(entity, name)) {
                            if (StringUtils.endsWith(name, INCLUDE)) {
                                name = name.substring(0, name.indexOf(START));
                                String string = (String) PropertyUtils.getSimpleProperty(entity, name);
                                if (org.apache.commons.lang3.StringUtils.isNotBlank(string)) {
                                    list.add(criteriaBuilder.like(root.get(name), string));
                                }
                            } else {
                                String string = (String) PropertyUtils.getSimpleProperty(entity, name);
                                if (org.apache.commons.lang3.StringUtils.isNotBlank(string)) {
                                    list.add(criteriaBuilder.equal(root.get(name), string));
                                }
                            }

                        }
                    }

                    else if (propertyDescriptor.getPropertyType().equals(Integer.class)) {
                        if (PropertyUtils.isReadable(entity, name)) {
                            if (StringUtils.endsWith(name, START)) {
                                name = name.substring(0, name.indexOf(START));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.greaterThanOrEqualTo(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, END)) {
                                name = name.substring(0, name.indexOf(END));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.lessThanOrEqualTo(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, AFTER)) {
                                name = name.substring(0, name.indexOf(AFTER));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.greaterThan(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, BEFORE)) {
                                name = name.substring(0, name.indexOf(BEFORE));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.lessThan(root.get(name), date));
                                }
                            } else {
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.equal(root.get(name), date));
                                }
                            }

                        }
                    }

                    else if (propertyDescriptor.getPropertyType().equals(Long.class)) {
                        if (PropertyUtils.isReadable(entity, name)) {
                            if (StringUtils.endsWith(name, START)) {
                                name = name.substring(0, name.indexOf(START));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.greaterThanOrEqualTo(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, END)) {
                                name = name.substring(0, name.indexOf(END));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.lessThanOrEqualTo(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, AFTER)) {
                                name = name.substring(0, name.indexOf(AFTER));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.greaterThan(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, BEFORE)) {
                                name = name.substring(0, name.indexOf(BEFORE));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.lessThan(root.get(name), date));
                                }
                            } else {
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.equal(root.get(name), date));
                                }
                            }

                        }
                    }

                    else if (propertyDescriptor.getPropertyType().equals(Double.class)) {
                        if (PropertyUtils.isReadable(entity, name)) {
                            if (StringUtils.endsWith(name, START)) {
                                name = name.substring(0, name.indexOf(START));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.greaterThanOrEqualTo(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, END)) {
                                name = name.substring(0, name.indexOf(END));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.lessThanOrEqualTo(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, AFTER)) {
                                name = name.substring(0, name.indexOf(AFTER));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.greaterThan(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, BEFORE)) {
                                name = name.substring(0, name.indexOf(BEFORE));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.lessThan(root.get(name), date));
                                }
                            } else {
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.equal(root.get(name), date));
                                }
                            }

                        }
                    }

                    else if (propertyDescriptor.getPropertyType().equals(BigDecimal.class)) {
                        if (PropertyUtils.isReadable(entity, name)) {
                            if (StringUtils.endsWith(name, START)) {
                                name = name.substring(0, name.indexOf(START));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.greaterThanOrEqualTo(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, END)) {
                                name = name.substring(0, name.indexOf(END));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.lessThanOrEqualTo(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, AFTER)) {
                                name = name.substring(0, name.indexOf(AFTER));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.greaterThan(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, BEFORE)) {
                                name = name.substring(0, name.indexOf(BEFORE));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.lessThan(root.get(name), date));
                                }
                            } else {
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.equal(root.get(name), date));
                                }
                            }

                        }
                    }

                    else if (propertyDescriptor.getPropertyType().equals(Date.class)) {
                        if (PropertyUtils.isReadable(entity, name)) {
                            if (StringUtils.endsWith(name, START)) {
                                name = name.substring(0, name.indexOf(START));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.greaterThanOrEqualTo(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, END)) {
                                name = name.substring(0, name.indexOf(END));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.lessThanOrEqualTo(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, AFTER)) {
                                name = name.substring(0, name.indexOf(AFTER));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.greaterThan(root.get(name), date));
                                }
                            } else if (StringUtils.endsWith(name, BEFORE)) {
                                name = name.substring(0, name.indexOf(BEFORE));
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.lessThan(root.get(name), date));
                                }
                            } else {
                                Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                                if (Objects.nonNull(date)) {
                                    list.add(criteriaBuilder.equal(root.get(name), date));
                                }
                            }

                        }
                    }

                    else {
                        Date date = (Date) PropertyUtils.getSimpleProperty(entity, name);
                        if (Objects.nonNull(date)) {
                            list.add(criteriaBuilder.equal(root.get(name), date));
                        }
                    }
                } catch (IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
                    e.printStackTrace();
                }


            });

            Predicate[] arr = new Predicate[list.size()];
            query.where(list.toArray(arr));
            return null;
        };
    }
}
